﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using CalcSharp.Core;

namespace ImageConvert
{
    public partial class Progress : Form
    {
        private Thread[] ConvertThread;
        private ConvThread[] thrs;

        private int pos;

        public Progress()
        {
            InitializeComponent();
            this.pos = 0;
        }

        public void UpdateProgress(int thread, string text)
        {
            pos += 1;
            this.ListCnv.Items.Add(text);
            this.ListCnv.SelectedIndex = this.ListCnv.Items.Count - 1;
        }

        public void CloseDialog()
        {
            StringBuilder errors = new StringBuilder();
            for (int i = 0; i < this.ConvertThread.Length; i++)
            {
                if (this.thrs[i].Errtext.Length != 0) errors.Append(this.thrs[i].Errtext);
                if (this.ConvertThread[i].IsAlive) this.ConvertThread[i].Abort();
            }
            this.Visible = false;
            this.Close();
            if (errors.Length == 0) MessageBox.Show("Conversion finished", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else MessageBox.Show(errors.ToString(), "Conversion Errors", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void Progress_Shown(object sender, EventArgs e)
        {
            int max = 0;
            this.ConvertThread = new Thread[ConvertOptions.Files.Length];
            this.thrs = new ConvThread[ConvertOptions.Files.Length];
            for (int i = 0; i < ConvertOptions.Files.Length; i++) max += ConvertOptions.Files[i].Count;
            this.ProgressBar.Maximum = max;
            this.BtnCancel.Enabled = true;
            this.ProgressBar.Minimum = 0;
            this.UpdTimer.Enabled = true;
            for (int i = 0; i < ConvertOptions.Files.Length; i++)
            {
                this.thrs[i] = new ConvThread(ConvertOptions.Files[i], this, i);
                this.ConvertThread[i] = new Thread(this.thrs[i].Run);
                this.ConvertThread[i].Start();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void Progress_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.ConvertThread != null)
            {
                if (this.ProgressBar.Value < this.ProgressBar.Maximum)
                {
                    DialogResult dr = MessageBox.Show("Stop converting process ?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (dr == DialogResult.Yes)
                    {
                        for (int i=0; i<this.ConvertThread.Length; i++) if (this.ConvertThread[i].IsAlive) this.ConvertThread[i].Abort();
                        Thread.Sleep(300);
                        e.Cancel = false;
                    }
                    else e.Cancel = true;
                }
            }
        }

        private void UpdTimer_Tick(object sender, EventArgs e)
        {
            this.ProgressBar.Value = pos;
            double percent = ((double)pos / this.ProgressBar.Maximum) * 100;
            percent = Math.Round(percent, 3);
            this.PercentLabel.Text = percent.ToString() + "%";
            if (this.ProgressBar.Value == this.ProgressBar.Maximum)
            {
                this.UpdTimer.Enabled = false;
                this.CloseDialog();
            }
        }

        private void BtnDetails_Click(object sender, EventArgs e)
        {
            if (this.Size == this.MinimumSize)
            {
                this.Size = this.MaximumSize;
                this.BtnDetails.Text = "Hide Details";
            }
            else
            {
                this.Size = this.MinimumSize;
                this.BtnDetails.Text = "Show Details";
            }
        }
    }

    public class ConvThread
    {
        public StringBuilder Errtext;
        private List<string> Files;
        private Progress GUI;
        private EncoderParameters encoderParams;
        private ImageCodecInfo jpegCodec;
        private delegate void Callback(int param, string text);
        private int number;

        public ConvThread(List<string> fil, Progress f, int n)
        {
            this.number = n;
            this.Files = fil;
            this.GUI = f;
            this.Errtext = new StringBuilder();
            this.encoderParams = new EncoderParameters(1);
            EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, ConvertOptions.JpegQuality);
            this.encoderParams.Param[0] = qualityParam;
            this.jpegCodec = GetEncoderInfo("image/jpeg");
        }

        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            for (int i = 0; i < codecs.Length; i++)
            {
                if (codecs[i].MimeType == mimeType) return codecs[i];
            }
            return null;
        }

        private string FnameSetup(string oldpath)
        {
            string path = Path.GetFileName(oldpath);
            return Path.ChangeExtension(path, ConvertOptions.TargetExt);
        }

        public void Run()
        {
            Bitmap cnv, blend;
            CrawDriver dr;
            TGADriver tg;
            HDPhotoDriver hd;
            PropertyItem[] Properties;
            string fname = null;

            blend = new Bitmap(1, 1);
            if (!string.IsNullOrEmpty(ConvertOptions.BlendImg))
            {
                if (ConvertOptions.BlendImg.ToLower().EndsWith(".craw"))
                {
                    dr = new CrawDriver(File.OpenRead(ConvertOptions.BlendImg));
                    blend = dr.ReadBitmap(true);
                }
                else blend = (Bitmap)Image.FromFile(ConvertOptions.BlendImg);
            }

            for (int i = 0; i < this.Files.Count; i++)
            {
                try
                {
                    fname = ConvertOptions.Outdir + @"\" + this.FnameSetup(Files[i]);
                    if (Files[i].ToLower().EndsWith(ConvertOptions.TargetExt) &&
                        ConvertOptions.TargetExt != ".jpg" &&
                        ConvertOptions.TargetExt != ".jpeg" &&
                        ConvertOptions.TargetExt != ".craw" &&
                        ConvertOptions.ResizeFactor == 1 &&
                        ConvertOptions.PxFormat == PixelFormat.DontCare)
                    {
                        if (!File.Exists(fname)) File.Copy(Files[i], fname);
                        else
                        {
                            File.Delete(fname);
                            File.Copy(Files[i], fname);
                        }
                    }
                    else
                    {
                        if (Files[i].ToLower().EndsWith(".craw"))
                        {
                            dr = new CrawDriver(File.OpenRead(Files[i]));
                            cnv = dr.ReadBitmap(true);
                        }
                        else if (Files[i].ToLower().EndsWith(".tga"))
                        {
                            tg = new TGADriver(File.OpenRead(Files[i]));
                            cnv = tg.ReadToBmp();
                        }
                        else if (Files[i].ToLower().EndsWith(".wdp"))
                        {
                            hd = new HDPhotoDriver(File.OpenRead(Files[i]));
                            cnv = hd.ReadBitmap();
                        }
                        else cnv = (Bitmap)Image.FromFile(Files[i]);
                        Properties = cnv.PropertyItems;

                        if (ConvertOptions.BlendMode != BlendMode.None) cnv = ImageTransform.MergeImages(cnv, blend, ConvertOptions.BlendMode);

                        if (ConvertOptions.PxFormat != PixelFormat.DontCare && ConvertOptions.ResizeFactor == 1)
                        {
                            if (ConvertOptions.PxFormat != cnv.PixelFormat)
                            {
                                cnv = ImageTransform.Resample(cnv, ConvertOptions.PxFormat);
                            }
                        }

                        if (ConvertOptions.ResizeFactor != 1)
                        {
                            int w = System.Convert.ToInt32(Math.Round(cnv.Width * ConvertOptions.ResizeFactor, 0));
                            int h = System.Convert.ToInt32(Math.Round(cnv.Height * ConvertOptions.ResizeFactor, 0));
                            cnv = ImageTransform.ResizeImage(cnv, w, h, ConvertOptions.InterpolMode, ConvertOptions.PxFormat);
                        }
                        if (File.Exists(fname)) File.Delete(fname);

                        if (ConvertOptions.KeepExif) foreach (PropertyItem pit in Properties) cnv.SetPropertyItem(pit);

                        switch (ConvertOptions.TargetExt)
                        {
                            case ".bmp":
                                cnv.Save(fname, ImageFormat.Bmp);
                                break;
                            case ".png":
                                cnv.Save(fname, ImageFormat.Png);
                                break;
                            case ".gif":
                                cnv.Save(fname, ImageFormat.Gif);
                                break;
                            case ".tif":
                                cnv.Save(fname, ImageFormat.Tiff);
                                break;
                            case ".emf":
                                cnv.Save(fname, ImageFormat.Emf);
                                break;
                            case ".wmf":
                                cnv.Save(fname, ImageFormat.Wmf);
                                break;
                            case ".jpg":
                                cnv.Save(fname, this.jpegCodec, this.encoderParams);
                                break;
                            case ".craw":
                                dr = new CrawDriver(File.Create(fname));
                                dr.WriteBitmap(cnv, ConvertOptions.MType, true);
                                break;
                            case ".wdp":
                                hd = new HDPhotoDriver(File.Create(fname));
                                hd.WriteBitmap(cnv, ConvertOptions.JpegQuality);
                                break;
                        }
                        cnv.Dispose();
                        cnv = null;
                        GC.Collect();
                        GUI.Invoke(new Callback(GUI.UpdateProgress), new object[] { this.number,  "Converted: "+fname });
                    }
                }
                catch (Exception)
                {
                    this.Errtext.Append(fname);
                    this.Errtext.Append("\r\n");
                    try
                    {
                        GUI.Invoke(new Callback(GUI.UpdateProgress), new object[] { this.number, "Failed to convert: " + fname });
                    }
                    catch (ObjectDisposedException) { }
                    catch (InvalidOperationException) { }
                }
            }
        }
    }
}
